﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Hsenl.Network {
    public class MessageOpcodeInfo {
        public ushort opcode;
        public byte messageKind; // 1: notif message, 2: request message, 3: response message
    }

    public class MessageTypeInfo {
        public Type type;
        public byte messageKind;
    }

    [FrameworkMember]
    public static class OpcodeLookupTable {
        private static readonly Dictionary<ushort, MessageTypeInfo> _messageTypeInfos = new();
        private static readonly Dictionary<Type, MessageOpcodeInfo> _messageOpcodeInfos = new();
        private static readonly BidirectionalDictionary<ushort, ushort> _rpcOpcodeMap = new(); // key: request, value: response
        private static readonly BidirectionalDictionary<Type, Type> _rpcTypeMap = new(); // key: request, value: response

        [OnEventSystemInitialized, Order(-300)]
        private static void InitOpcodes() {
            _messageOpcodeInfos.Clear();
            _messageTypeInfos.Clear();
            _rpcOpcodeMap.Clear();
            _rpcTypeMap.Clear();
            var messageTypes = EventSystem.GetTypesOfAttribute(typeof(MessageAttribute), true);
            ushort opcode = 1;
            foreach (var type in messageTypes) {
                var attr = type.GetCustomAttribute<MessageAttribute>();
                if (attr == null)
                    continue;

                if (attr is MessageRequestAttribute requestAttribute) {
                    _messageOpcodeInfos.Add(type, new MessageOpcodeInfo() { opcode = opcode, messageKind = 2 });
                    _messageTypeInfos.Add(opcode, new MessageTypeInfo() { type = type, messageKind = 2 });
                    if (requestAttribute.ResponseType == null) {
                        Log.Error($"The response message to the request message is not declared '{type}'"); // Rpc消息必须成对声明
                    }
                    else {
                        if (_rpcTypeMap.ContainsValue(requestAttribute.ResponseType)) {
                            Log.Error($"Response message is already exist '{requestAttribute.ResponseType}'"); // 两个 请求消息 指定了同一个 回复消息
                        }
                        else {
                            _rpcTypeMap.Add(type, requestAttribute.ResponseType);
                        }
                    }
                }
                else if (attr is MessageResponseAttribute) {
                    _messageOpcodeInfos.Add(type, new MessageOpcodeInfo() { opcode = opcode, messageKind = 3 });
                    _messageTypeInfos.Add(opcode, new MessageTypeInfo() { type = type, messageKind = 3 });
                }
                else {
                    _messageOpcodeInfos.Add(type, new MessageOpcodeInfo() { opcode = opcode, messageKind = 1 });
                    _messageTypeInfos.Add(opcode, new MessageTypeInfo() { type = type, messageKind = 1 });
                }

                opcode++;
            }

            foreach (var kv in _rpcTypeMap) {
                var info1 = _messageOpcodeInfos[kv.Key];
                var info2 = _messageOpcodeInfos[kv.Value];
                _rpcOpcodeMap.Add(info1.opcode, info2.opcode);
            }
        }

        public static MessageTypeInfo GetMessageTypeInfo(ushort opcode) {
            if (_messageTypeInfos.TryGetValue(opcode, out var info)) {
                return info;
            }

            throw new KeyNotFoundException($"The message opcode {opcode} was not found");
        }

        public static MessageOpcodeInfo GetMessageOpcodeInfo(Type type) {
            if (_messageOpcodeInfos.TryGetValue(type, out var info)) {
                return info;
            }

            throw new KeyNotFoundException($"The message type {type} was not found");
        }

        public static Type GetMessageType(ushort opcode) {
            if (_messageTypeInfos.TryGetValue(opcode, out var info)) {
                return info.type;
            }

            throw new KeyNotFoundException($"The message opcode {opcode} was not found");
        }

        public static ushort GetMessageOpcode(Type type) {
            if (_messageOpcodeInfos.TryGetValue(type, out var info)) {
                return info.opcode;
            }

            throw new KeyNotFoundException($"The message type {type} was not found");
        }

        public static Type GetResponseTypeOfRequestType(Type type) {
            if (_rpcTypeMap.TryGetValueByKey(type, out var result)) {
                return result;
            }

            throw new KeyNotFoundException($"The message type {type} was not found");
        }

        public static Type GetRequestTypeOfResponseType(Type type) {
            if (_rpcTypeMap.TryGetKeyByValue(type, out var result)) {
                return result;
            }

            throw new KeyNotFoundException($"The message type {type} was not found");
        }

        public static ushort GetResponseOfRequest(ushort opcode) {
            if (_rpcOpcodeMap.TryGetValueByKey(opcode, out var result)) {
                return result;
            }

            throw new KeyNotFoundException($"The message opcode {opcode} was not found");
        }

        public static ushort GetRequestOfResponse(ushort opcode) {
            if (_rpcOpcodeMap.TryGetKeyByValue(opcode, out var result)) {
                return result;
            }

            throw new KeyNotFoundException($"The message opcode {opcode} was not found");
        }

        public static bool IsRequestOpcode(ushort op) {
            return _rpcOpcodeMap.ContainsKey(op);
        }

        public static bool IsResponseOpcode(ushort op) {
            return _rpcOpcodeMap.ContainsValue(op);
        }

        public static bool IsRequestType(Type type) {
            return _rpcTypeMap.ContainsKey(type);
        }

        public static bool IsResponseType(Type type) {
            return _rpcTypeMap.ContainsValue(type);
        }
    }
}